﻿#include "dataserialport.h"
#include <QThread>
#ifdef QT_DEBUG
#include <QDebug>
#include <QDateTime>
#endif

//QSemaphore m_semEcho(1);
DataSerialPort::DataSerialPort() : QObject(),
  m_strComName(""),
  m_pThread(new QThread()),
  m_pCom(new QSerialPort()),
  m_iLen(-1),
  m_bOpen(false),
  m_bEchoFlag(false)
{
    qRegisterMetaType<tagSerialPortInfo>("tagSerialPortInfo");
    m_pCom->moveToThread(m_pThread);
    this->moveToThread(m_pThread);
    m_pThread->start();
    connect(m_pCom, &QSerialPort::readyRead, this, &DataSerialPort::slotDataReady, Qt::DirectConnection);

    connect(this, &DataSerialPort::sigSetCOM, this, &DataSerialPort::slotSetCOM);
    connect(this, &DataSerialPort::sigClose, this, &DataSerialPort::slotClose);
    connect(this, &DataSerialPort::sigClear, this, &DataSerialPort::slotClear);
    connect(this, &DataSerialPort::sigWrite, this, &DataSerialPort::slotWrite);
}

DataSerialPort::~DataSerialPort()
{
#ifdef QT_DEBUG
    qDebug() << "~DataSerialPort:" << QThread::currentThreadId() << "\n";
#endif
    close();
    m_pThread->quit();
    m_pThread->wait();
    delete m_pCom;
    m_pCom = 0;
    delete m_pThread;
    m_pThread = 0;
}

bool DataSerialPort::isOpen() const
{
    return m_bOpen;
}

qint64 DataSerialPort::write(const QByteArray &byteArray)
{
    return write(byteArray.data(), byteArray.size());
}

qint64 DataSerialPort::write(const char *data, qint64 maxSize/* = -1*/)
{
    if(!m_bOpen) return -1;
    m_lockWrite.lock();
    const int nAvlb = m_semWrite.available();
    if(nAvlb > 0)
    {
        m_semWrite.tryAcquire(nAvlb);
    }
    emit sigWrite(data, maxSize);
    const bool bWait = m_semWrite.tryAcquire(1, 5000);
    m_lockWrite.unlock();
    QMutexLocker lk(&m_lockWriteLen);
    qint64 iRet = bWait ? m_iLen : 0;
    return iRet;
}

void DataSerialPort::close()
{
    m_lockClose.lock();
    const int nAvlb = m_semClose.available();
    if(nAvlb > 0)
    {
        m_semClose.tryAcquire(nAvlb);
    }
    emit sigClose();
    m_semClose.tryAcquire(1, 5000);
    m_lockClose.unlock();
}

void DataSerialPort::clear()
{
    m_lockClear.lock();
    const int nAvlb = m_semClear.available();
    if(nAvlb > 0)
    {
        m_semClear.tryAcquire(nAvlb);
    }
    emit sigClear();
    m_semClear.tryAcquire(1, 5000);
    m_lockClear.unlock();
}

bool DataSerialPort::sendReciveData(const char * strCmd, qint64 leng, QByteArray& pstrReply)
{
    clear();
    setEchoFlag(true);
    const int nAvlb = m_semEcho.available();
    if(nAvlb > 0)
    {
        m_semEcho.tryAcquire(nAvlb);
    }

    pstrReply.clear();
    if(write(strCmd,leng) < leng)
    {
        setEchoFlag(false);
        return false;
    }

    bool bRet = m_semEcho.tryAcquire(1, 10000);


#ifdef QT_DEBUG
    qDebug() << QDateTime::currentDateTime().toString("HH:mm:ss.zzz") << "EchoCommand wait " << bRet << " " << m_strReply << " |\n";
#endif
    setEchoFlag(false);
    if(false == bRet)
    {
        if(m_strReply.endsWith('\n'))
        {
            bRet = true;
        }
    }

    pstrReply = m_strReply.toLatin1();

    return bRet;
}
bool DataSerialPort::openPort(tagSerialPortInfo& cfg)
{
    m_lockSetCOM.lock();
    const int nAvlb = m_semSetCOM.available();
    if(nAvlb > 0)
    {
        m_semSetCOM.tryAcquire(nAvlb);
    }
    emit sigSetCOM(cfg);
    const bool bWait = m_semSetCOM.tryAcquire(1, 5000);
    m_lockSetCOM.unlock();
    return bWait ? m_bOpen : false;

}

void DataSerialPort::slotSetCOM(tagSerialPortInfo cfg)
{
    qDebug()<<"slotSetCOM thread id = "<<QThread::currentThreadId()<<"==============================";
    m_pCom->close();
    m_bOpen = false;

    if (cfg.portName.length() < 1)
        return;

    m_pCom->setPortName(cfg.portName);
    if(!m_pCom->setBaudRate(cfg.baudRate, cfg.directions)) return ;
    if(!m_pCom->setDataBits(cfg.dataBits)) return ;
    if(!m_pCom->setFlowControl(cfg.flowControl)) return;
    if(!m_pCom->setParity(cfg.parity)) return;
    if(!m_pCom->setStopBits(cfg.stopBits)) return;
    if(!m_pCom->open(QIODevice::ReadWrite))
    {
        emit sigCatchException("serialport open failture" + m_pCom->errorString());
        qDebug() << "serialport open failture" << m_pCom->errorString();
        m_semSetCOM.release();
        return ;
    }

    m_pCom->clear();
    m_bOpen = true;
    m_semSetCOM.release();
}
void DataSerialPort::slotWrite(const char *pch, qint64 maxSize)
{
    //qDebug() << QDateTime::currentDateTime().toString("HH:mm:ss.zzz") << " write begin\n";
    m_lockWriteLen.lock();
    m_iLen = -1;
    if(m_pCom->isOpen())
    {
        m_iLen = (maxSize < 0) ? m_pCom->write(pch) : m_pCom->write(pch, maxSize);
    }
    m_lockWriteLen.unlock();
    m_semWrite.release();
    //qDebug() << QDateTime::currentDateTime().toString("HH:mm:ss.zzz") << " write OK\n";
}
void DataSerialPort::slotClose()
{
    //qDebug() << "slotClose:" << QThread::currentThreadId() <<"\n";
    m_pCom->close();
    m_bOpen = false;
    m_semClose.release();
}
void DataSerialPort::slotClear()
{
    m_pCom->clear();
    m_lockInBuffer.lock();
    m_strInBuffer.clear();
    m_strReply.clear();
    m_lockInBuffer.unlock();
    m_semClear.release();
}
void DataSerialPort::slotDataReady()
{
    qDebug()<<"slotDataReady thread id = "<<QThread::currentThreadId()<<"==============================";
    QSerialPort *const pCom = m_pCom;
    if(getEchoFlag())
    {
        m_strInBuffer.push_back(pCom->readAll());
        m_strReply = m_strInBuffer;

        setEchoFlag(false);
        m_semEcho.release();
    }
    else
    {
        emit sigDataReady(pCom->readAll());
    }
}
